/*
 * Copyright (C) 2010 Cyril Mottier (http://www.cyrilmottier.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package greendroid.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.cyrilmottier.android.gdcatalog.R;

/**
 * A SegmentedBar displays a set of Buttons. Only one segment in a SegmentedBar
 * can be selected at a time. A SegmentedBar is very close to a TabWidget in
 * term of functionalities.
 * 
 * @author Cyril Mottier
 */
public class SegmentedBar extends LinearLayout implements OnFocusChangeListener {

	/**
	 * Clients may use this listener to be notified of any changes that occurs
	 * on the SegmentBar
	 * 
	 * @author Cyril Mottier
	 */
	public static interface OnSegmentChangeListener {
		/**
		 * Notification that the current segment has changed.
		 * 
		 * @param index
		 *            The index of the new selected segment.
		 * @param clicked
		 *            Whether the segment has been selected via a user click.
		 */
		public void onSegmentChange(int index, boolean clicked);
	}

	private OnSegmentChangeListener mOnSegmentChangeListener;
	private int mCheckedSegment;
	private Drawable mDividerDrawable;
	private int mDividerWidth;

	public SegmentedBar(Context context) {
		this(context, null);
	}

	public SegmentedBar(Context context, AttributeSet attrs) {
		this(context, attrs, R.attr.gdSegmentedBarStyle);
	}

	public SegmentedBar(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs);
		initSegmentedBar();

		TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SegmentedBar, defStyle, 0);

		mDividerDrawable = a.getDrawable(R.styleable.SegmentedBar_dividerDrawable);
		mDividerWidth = a.getDimensionPixelSize(R.styleable.SegmentedBar_dividerWidth, 0);

		a.recycle();
	}

	private void initSegmentedBar() {
		mCheckedSegment = 0;
		setOrientation(LinearLayout.HORIZONTAL);

		// Register ourselves so that we can handle focus on internal segments
		setFocusable(true);
		setOnFocusChangeListener(this);
	}

	/**
	 * Sets the drawable that is used as divider between each segment.
	 * 
	 * @param dividerDrawable
	 *            The drawable to used as a divider. Note : using a
	 *            ColorDrawable will not work properly as the intrinsic width of
	 *            a ColorDrawable is -1.
	 */
	public void setDividerDrawable(Drawable dividerDrawable) {
		mDividerDrawable = dividerDrawable;
	}

	/**
	 * Sets the drawable that is used as divider between each segment.
	 * 
	 * @param resId
	 *            The identifier of the Drawable to use.
	 */
	public void setDividerDrawable(int resId) {
		mDividerDrawable = getContext().getResources().getDrawable(resId);
	}

	/**
	 * Sets the width of the divider that will be used as segment divider. If
	 * the dividerWidth has not been set, the intrinsic width of the divider
	 * drawable is used.
	 * 
	 * @param width
	 *            Width of the divider
	 */
	public void setDividerWidth(int width) {
		mDividerWidth = width;
	}

	/**
	 * Returns the current number of segment in this SegmentBar
	 * 
	 * @return The number of segments in this SegmentBar
	 */
	public int getSegmentCount() {
		int segmentCount = getChildCount();
		// If we have divider we'll have an odd number of child
		if (mDividerDrawable != null) {
			segmentCount = (segmentCount + 1) / 2;
		}
		return segmentCount;
	}

	/**
	 * Use this method to register an OnSegmentChangeListener and listen to
	 * changes that occur on this SegmentBar
	 * 
	 * @param listener
	 *            The listener to use
	 */
	public void setOnSegmentChangeListener(OnSegmentChangeListener listener) {
		mOnSegmentChangeListener = listener;
	}

	/**
	 * Sets the current segment to the index <em>index</em>
	 * 
	 * @param index
	 *            The index of the segment to set. Client will be notified using
	 *            this method of the segment change.
	 */
	public void setCurrentSegment(int index) {
		if (index < 0 || index >= getSegmentCount()) {
			return;
		}

		((CheckBox) getChildSegmentAt(mCheckedSegment)).setChecked(false);
		mCheckedSegment = index;
		((CheckBox) getChildSegmentAt(mCheckedSegment)).setChecked(true);
	}

	/**
	 * Returns the view representing the segment at the index <em>index</em>
	 * 
	 * @param index
	 *            The index of the segment to retrieve
	 * @return The view that represents the segment at index <em>index</em>
	 */
	public View getChildSegmentAt(int index) {
		/*
		 * If we are using dividers, then instead of segments at 0, 1, 2, ... we
		 * have segments at 0, 2, 4, ...
		 */
		if (mDividerDrawable != null) {
			index *= 2;
		}
		return getChildAt(index);
	}

	/**
	 * Adds a segment to the SegmentBar. This method automatically adds a
	 * divider if needed.
	 * 
	 * @param title
	 *            The title of the segment to add.
	 */
	public void addSegment(String title) {

		final Context context = getContext();
		final LayoutInflater inflater = LayoutInflater.from(context);

		/*
		 * First of all, we have to check whether or not we need to add a
		 * divider. A divider is added when there is at least one segment
		 */
		if (mDividerDrawable != null && getSegmentCount() > 0) {
			ImageView divider = new ImageView(context);
			final int width = (mDividerWidth > 0) ? mDividerWidth : mDividerDrawable.getIntrinsicWidth();
			final LinearLayout.LayoutParams lp = new LayoutParams(width, LayoutParams.FILL_PARENT);
			lp.setMargins(0, 0, 0, 0);
			divider.setLayoutParams(lp);
			divider.setBackgroundDrawable(mDividerDrawable);
			addView(divider);
		}

		CheckBox segment = (CheckBox) inflater.inflate(R.layout.gd_segment, this, false);
		segment.setText(title);

		segment.setClickable(true);
		segment.setFocusable(true);
		segment.setOnFocusChangeListener(this);
		segment.setOnCheckedChangeListener(new SegmentCheckedListener(getSegmentCount()));
		segment.setOnClickListener(new SegmentClickedListener(getSegmentCount()));

		addView(segment);
	}

	public void onFocusChange(View v, boolean hasFocus) {

		if (!hasFocus) {
			return;
		}

		if (v == this) {
			final View segment = getChildSegmentAt(mCheckedSegment);
			if (segment != null) {
				segment.requestFocus();
			}
		}

		else {
			final int segmentCounts = getSegmentCount();
			for (int i = 0; i < segmentCounts; i++) {
				if (getChildSegmentAt(i) == v) {
					setCurrentSegment(i);
					notifyListener(i, false);
					break;
				}
			}
		}
	}

	private class SegmentClickedListener implements OnClickListener {

		private final int mSegmentIndex;

		private SegmentClickedListener(int segmentIndex) {
			mSegmentIndex = segmentIndex;
		}

		public void onClick(View v) {
			final CheckBox segment = (CheckBox) getChildSegmentAt(mCheckedSegment);
			if (mSegmentIndex == mCheckedSegment && !segment.isChecked()) {
				segment.setChecked(true);
			} else {
				notifyListener(mSegmentIndex, true);
			}
		}

	}

	private class SegmentCheckedListener implements OnCheckedChangeListener {

		private final int mSegmentIndex;

		private SegmentCheckedListener(int segmentIndex) {
			mSegmentIndex = segmentIndex;
		}

		public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
			if (isChecked) {
				setCurrentSegment(mSegmentIndex);
			}
		}
	}

	private void notifyListener(int index, boolean clicked) {
		if (mOnSegmentChangeListener != null) {
			mOnSegmentChangeListener.onSegmentChange(index, clicked);
		}
	}
}
